Skip to content

A node depends on an interface or exposes one: retire Input and Output, rename the module body's inputs to deps - #398

Merged
wmadden merged 7 commits into
claude/resolve-values-ca39d3from
claude/interface-vocabulary
Oct 29, 2026
Merged

wmadden merged 7 commits into
claude/resolve-values-ca39d3from
claude/interface-vocabulary

Conversation

@wmadden-electric

Copy link
Copy Markdown
Contributor

At a glance

A module body receives its dependencies as deps, the same word the boundary declares them with:

export default module('auth', { deps: { db: rawPostgres() }, expose: { verify: authContract } },
  ({ deps, provision }) => {                                  // was ({ inputs, provision })
    const svc = provision(authService, { id: 'api', deps: { db: deps.db } });
    return { verify: svc.verify };
  });

And the design documents describe the same thing in the same words. The glossary, before and after:

A connection is an edge that wires one node's Output to another node's Input.

A connection is an edge that wires one node's dependency to another node's exposed interface.

What this PR changes

  1. A new decision record: a node depends on an interface or exposes one, and Input and Output are retired as the words for the two ends of a connection.
  2. The glossary defines Interface, Dependency and Exposed interface, and loses its Input, Output and Port entries. The domain map, the layering map, the authoring-surface narrative, the example app, the guiding principles and the domain docs use the new words.
  3. ModuleContext.inputs is renamed deps. The shared modules, the examples, the tests, the guide and the agent skill follow, with an upgrade note in the guide.
  4. The application topology's direction field keeps in and out, now documented as the dependency end and the exposed end.

Why the words were ambiguous

Take a service web that offers an HTTP API and needs a database. Composer's documents named the two ends by dataflow: the API is an output, because other nodes call web through it, and the database is an input, because web calls it. Ports and adapters, the architecture most readers bring, names the same two ends the other way round: the API is an input port, because calls come into web through it, and the database is an output port, because calls go out through it.

Both are consistent. Every end has both aspects, who provides it and which way calls flow, and the two traditions weigh them differently. So a reader from either tradition read half the sentences in the glossary backwards. The design discussion behind this change went through exactly that, sentence by sentence, until it was clear the words were the problem rather than either reader.

The words that name the relationship

Whether a node depends on an interface or exposes one is a fact about the node, not a direction the reader has to pick. The authoring surface already said it: deps and expose. The record makes those the vocabulary:

  • An interface is the typed thing a connection carries. In code it is a Contract, which keeps its name. An interface in Composer's terms is what ports and adapters calls a port; that analogy appears once, in the glossary, and "port" is not otherwise used, because Composer also has HTTP and TCP ports.
  • A dependency is a node's need for an interface, declared with deps.
  • An exposed interface is one a node offers, declared with expose; a resource exposes the one interface it provides, and a node may expose nothing.
  • A connection wires a dependency to an exposed interface.

"Input" keeps its other meaning, the configuration document a service or module declares as input. That is not an end of a connection, and the glossary says so once.

The one user-facing rename

The module body's inputs was the only authoring-surface word that called a dependency an input. It is now deps, so the body reads like the boundary it forwards: ({ deps, provision }) => provision(svc, { deps: { db: deps.db } }). The guide's module section carries the upgrade note. Two internal names keep the old word, ServiceNode.inputs and InputRef; renaming them reaches into the Prisma Cloud extension and the local target, so they are recorded as debt in .drive/deferred.md.

The topology's wire field

The application topology sends each end's direction as in or out to the platform, and the Console draws them. The field is not renamed: it belongs to the Portal team's API, and its meaning is unambiguous once defined. in is a dependency, out is an exposed interface, and the type's doc comment now says so.

Tests

No behaviour changes. The rename is covered by the existing module-composition, authored-graph, resource-slot and application-topology tests, which now destructure deps; pnpm typecheck across all 80 packages proves no caller was missed, and the full pnpm test suite passes. The website content test proves every guide link still resolves, and the skill packaging check proves the skill is still the one that ships.

Alternatives considered

  • Keep Input and Output, defined by the dataflow reading. They were already defined that way, and the discussion showed the definition does not survive contact with a reader from the other tradition.
  • Flip Input and Output to the ports-and-adapters reading. Renames ctx.inputs anyway, flips the meaning of a public API field, and moves every end in the Console to the other side of its card, for words that stay ambiguous to the next reader.
  • Provided and required interface (UML's ball-and-socket). Sound, but the authoring surface says deps and expose, and "provider" already means an Alchemy provider here.
  • Driving and driven ports (Cockburn). Nobody on the authoring surface says "driven", and "port" is out.
  • Rename expose to provides so services match resource({ provides }). A user-facing rename for consistency between a keyword and a verb the prose no longer leans on; left for another day.

Agent: hermione-26

🤖 Generated with Claude Code

wmadden-electric and others added 4 commits October 29, 2026 13:37
Input and Output were the words for the two ends of a connection, and
they were ambiguous: the dataflow reading and the ports-and-adapters
reading give the same two ends opposite names. The record settles the
words the authoring surface already uses, deps and expose, with
interface as the noun, and retires the port words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
…tput

The glossary defines Interface, Dependency and Exposed interface in place
of Input, Output and Port; the domain map, layering map, authoring
surface, example app, principles and domain docs use the same words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
The one user-facing place that called a dependency an input. The shared
modules, the example and the tests follow; the topology's port direction
field says what in and out mean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
@prisma-gizmo

prisma-gizmo Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

✅ Gizmo reviewed 885170a — posted 0 inline comment(s) this pass.

Open findings: none

Change walkthrough

This PR retires the Input/Output vocabulary across Composer in favor of dependency / exposed interface — the words the authoring surface already uses (deps / expose) — because the old terms flip meaning between the dataflow and ports-and-adapters reading traditions. A new ADR records the decision, the glossary defines the three new terms, and the one user-facing rename renames ModuleContext.inputs to deps.

Terminology and docs. The glossary, guiding principles, domain map, layering map, example-app narrative, and domain docs now describe a connection as wiring a node's dependency to another node's exposed interface. "Input" survives only in its unrelated meaning — the configuration document declared as input — and the glossary says so once. The example app's tables changed from Inputs/Outputs columns to Depends on/Exposes.

Authoring surface. ModuleContext.inputs is renamed deps so the module body reads like the boundary it forwards (({ deps, provision }) => provision(svc, { deps: { db: deps.db } })). Shared modules, examples, tests, the guide (with an upgrade note), and the agent skill follow the rename. The rename is type-level, and the PR reports pnpm typecheck across all 80 packages plus the full test suite passing, so no caller was missed.

Deliberately unchanged. Two internal names keep the retired word — ServiceNode.inputs and InputRef — because renaming them reaches into the Prisma Cloud extension and the local target; they are recorded as debt in .drive/deferred.md. The application topology's direction: 'in' | 'out' field keeps its wire values since they belong to the Portal team's API; its doc comment now defines in as the dependency end and out as the exposed end. The PR's alternatives section explains why the UML and Cockburn vocabularies and an expose→provides rename were considered and rejected.

Tests. No behavior changes. The composition, authored-graph, resource-slot, and application-topology tests now destructure deps; in core, the lone-service error test's title was updated to stop calling a dependency "the input" (module.test.ts:287), matching the unwired dependency wording the LoadError itself emits.

@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: e644ed86-cf55-4948-816a-ede467a04d9f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@prisma/composer@398
npm i https://pkg.pr.new/@prisma/composer-cli@398
npm i https://pkg.pr.new/@prisma/composer-prisma-cloud@398

commit: 885170a

@prisma-gizmo prisma-gizmo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New findings: 🟡 2 minor · trace

wmadden-electric and others added 2 commits October 29, 2026 13:47
…interface

The error texts still called a declared dependency an input and an exposed
interface an output, and the skill still said ports.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

@prisma-gizmo prisma-gizmo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New findings: 🟡 1 minor · trace

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

@prisma-gizmo prisma-gizmo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New findings: none · trace

@prisma-gizmo prisma-gizmo Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No Gizmo finding is open and the head commit has been reviewed. Approving.

@wmadden
wmadden merged commit ae6bcfa into claude/resolve-values-ca39d3 Oct 29, 2026
21 of 22 checks passed
@wmadden
wmadden deleted the claude/interface-vocabulary branch October 29, 2026 16:36
wmadden pushed a commit that referenced this pull request Oct 29, 2026
…ond pass; a resource may carry its edge and props (#396)

* docs(design): propose ingress, module input and per-stage configuration (ADR-0060 to ADR-0062)

ADR-0060: public HTTP traffic reaches a service only through an
ingress(), a driver on an exposed HTTP port.
ADR-0061: a module declares its input as one schema, as a compute
service does; the root may declare input.
ADR-0062: per-stage configuration comes from providers named in the
composer section; every stage has a name.

Adds the Prisma Cloud ingress doc and amendment notes on the ADRs,
index and glossary entries these change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): link the Prisma Cloud ingress doc to the PDP proposal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): point domain docs at ADR-0061 and ADR-0062; label ADR-0016 as amended

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): restate the stage rule as a fixed graph structure for any input

ADR-0024's "the stage never appears in the authored topology" becomes
what it protects: the graph's structure is the same whatever inputs
Composer is given. Code may select values per stage, resolved after the
graph is built with provision disabled, never structure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): make the ADR-0061 notes on core-model and module-composition accurate

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): public URLs, resolve(), environments (ADR-0060 to ADR-0062 rewritten)

Applies the team discussion: the node is publicUrl() with two explicit
strategies on it; a per-environment or derived value is computed with
resolve(deps, fn) after the graph is built; "stage" becomes
"environment"; config adapters bring in outside values but no longer
carry the URL story; the platform gains Public URL and Domain records.
Adds the public URL domain map and scenarios as companion drafts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): link ADR-0062 to the renamed ADR-0059

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): fix a nested resolve(), scenario 9's absent key, and leftover stage wording

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): drop duplicated index annotations from the merge

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): settle the publicUrl strategy names, bare-string shorthand and local-dev warning

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): public URLs project spec and plan

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): an input reference may be passed anywhere a value is accepted; drop the DNS-label row

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): align ADR-0061's spike bullet with the amended rule

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): public URLs project in the Drive shape

Spec with non-goals, place in the larger world, cross-cutting
requirements, transitional-shape constraints and a verifiable project
DoD; design notes for the execution decisions the ADRs don't carry;
the plan cut to eight slices with owners. Module input and config
adapters become a follow-on project, recorded in .drive/deferred.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): resolve() lists the environment as a dependency and receives one tuple

The environment's name is no longer an extra argument to fn: it is a
reference, environment.name, listed like an input or another resolved
value, so new deploy-time facts add references, not parameters. fn
receives the listed values as one tuple, the shape of Vue's watch and
Solid's on; the useMemo analogy is dropped. The dependency list is kept
because it is the only way one resolved value can be computed from
another, and it gives Composer a graph to order and check for cycles.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): finish the resolve() tuple change in scenario 4 and the deferred entry

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the platform records are ServiceUrl and domain ownership

Names and shape from the platform design discussion of 2026-10-27:
one ServiceUrl entity for internal and external reach (named or
assigned, public or private, several per service, conditions instead
of a status, reconciled by the control plane over an unchanged
Foundry); a domain ownership record on the workspace claiming an exact
name or wildcard with today's TXT value; nothing created unless asked,
with Composer declaring private URLs for called services and turning
the platform's default URL off. The Drive spec and plan point D1 at
the control plane's own project, projects/service-urls.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): the Alchemy resource is Prisma.ServiceUrl

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): P2 owns the post-transition lowering; design notes match the plan's owners

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): rewrite ADR-0060, 0061 and 0062 for a reader without our context

Each now opens with the decision and a grounding example that builds up
one idea at a time, keeps the reasoning in the present tense, and ends
with the alternatives. Project language (spikes, migrations of the
shipped modules), transitional states (--stage replaced, the retired
destroy target, 'for now' platform behaviour) and process references are
gone; the stale 'per project sharing the Domain' uniqueness rule is now
platform-wide.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): fix a doubled full stop in the index

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): replace ASCII diagrams with tables and prose

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): U1 needs Alchemy's live test; C2 waits for the environment decision

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the platform record is RoutingUrl; the ownership field is domain

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the first platform step covers custom hostnames only

A public URL with a named hostname becomes a RoutingUrl record; a generated URL
creates nothing, because every service keeps the platform's hostname. Domain
ownership is a claim, verified on conflict. A hostname is unique at
registration, not at creation. Resolved values never set a node's identity.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): ownership is a claim, not a proof

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): scenario 4 follows the registration-time rule; consistent terms

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): dev takes --environment; the index matches the amended public URL decision

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the deploy CLI doc lists --environment on prisma dev

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): the endpoint as a routing URL is step two of this project

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the strategy is publicUrl.prismaBuild(); the Alchemy resource adopts existing rows

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): scenario 3 is the adoption rule's plain case; prismaBuild() lowers to a row in step two

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): adoption applies to custom hostnames too

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): a Console record is touched only to adopt it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): one document states the whole public-URL proposal, with a reading order

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): every public-URL document in one folder

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the public-URL proposal in full sentences

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the proposal starts from an example, maps the three domains, and walks the key operations

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the proposal opens with the user's problem

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(drive): links to the moved public-URL documents resolve

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the RoutingUrl repository keeps Foundry consistent; Foundry's words throughout

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the domain map names the six conditions as the design does

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): queued retry and the Console check button in place of the cron

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): one press completes a hostname; waiting rows get the hostname without a user; scope wording

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): one press completes a hostname; waiting rows get a freed hostname without a user

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): bounded recreate; provisioning follow-up runs for the issuance timeout

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform lifecycle copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the promote diagram's requeue note matches the policy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform lifecycle copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the row records when its custom domain was created

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the issuance clock starts at creation or the latest retry

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): link the platform's consistency rule from the proposal

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): name the teams: Terminal, Portal, Metal

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the proposal links the general consistency design and both surveys

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): link the consistency overview from the proposal

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): Mermaid messages without semicolons or angle brackets

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the systems below the control plane are the data plane

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies with the observed columns

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): plan the project as four user increments

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): specify and plan slice C1, resolve() and the second pass

The slice contract for resolve(): the value type, where it is accepted,
the second evaluation pass that seals provision and substitutes results,
the environment name's path into Load, and a resource that carries its
dependency edge from construction, which the public URL node needs next.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 also resolves values inside a resource's options

A public URL's url is a parameter of a resource-kind node, not a service
input, so the second pass must substitute there too. The resource carries
a plain-data options bag the factory records; the graph holds a resolved
copy with its prototype kept.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): a deploy prints the records to publish; the platform creates the claim on verification; DNS adapter is a follow-on

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the deploy output prints the records and no Console link; refresh the platform copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the scenarios and the overview print the records, not a Console link; refresh the platform copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): refresh the platform copy

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs: move the public-URLs proposal under the project folder until close-out

The public design documentation under docs/design/ receives material
only when a project closes and its design is final. The proposal, the
domain map, the scenarios and the copies of the platform design belong
to the project while it is open.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 spec pins values, resolve() and the second pass in full

The slice contract after the design discussion with Will on 2026-10-29:
the thing resolve() returns is a value, provided or computed, deploy-time
or runtime; the public URL node is a resource that carries its edge and
options from construction. Every type, signature, error message, file and
test is pinned so the implementer decides nothing. The decision records
for resolve(), public URLs and resources carry amendment notes, the
glossary defines the term, and the project docs use it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 spec folds in the architect and principal-engineer reviews

The two design reviews found four mechanisms that fail as pinned (the
frozen-node copy, sealing nested modules, values inside non-plain objects,
a user's thrown error reported as a Composer bug) and several names that
contradict the vocabulary. The spec now pins the fixes: one plain-data
predicate for the walk and the result check, one shared seal flag, a copy
built from descriptors, a LoadError with cause, purity of fn, optional
environment on lowering options, real value as the term for what a value
stands for, and the kind-and-role reconciliation of resource and driver.
Four points are recorded as pending Will.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 spec records the four settled points

A resource keeps provides (a public URL provides its ingress contract);
its bound producers are deps, as at provision(); its configuration is
props; a computed value may not return a secret or param source. The
port vocabulary question Will raised is recorded as its own decision
record to be written beside the slice.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: add the Value type and resolve()

A module body needs a stand-in for data that is only known per
environment. Value<T> is that stand-in: opaque to the body, branded with
Symbol.for so a check survives duplicated module instances, and carrying
its origin under a symbol key so the body has no member to read.
resolve(dependencies, fn) declares a computed value; environment.name is
the one provided value.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: Load resolves every bound value in a second pass

A service input binding and a service param now accept a value, and the
module context hands every body environment.name. After the first pass
builds and validates the graph, Load seals provision() and resolves each
bound value dependency-first, once each, then substitutes the real values
into the bindings. Load takes the environment's name through a new
LoadOptions.environment.

A value can configure a node but never shape the graph, so provision()
refuses one as an id, in deps, or as a module secrets or params binding,
and provision() called from inside a resolve() function fails. LoadError
gains an optional cause so a user's thrown error is wrapped exactly once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: lowering() passes the environment's name to Load

LowerOptions.environment replaces the unread stage option, so the graph
the stack file lowers has its values resolved for the environment being
deployed. It stays optional: a hand-composed stack that binds no value
needs none.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 spec records how D1 landed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: a resource may declare slots wired at construction and carry props

ResourceNode gains inputs, deps (the producers wired into those slots) and
props (plain data and values). resource() and ResourceNodeBase take them
and check that deps wire exactly the declared inputs, never to a value,
and that props is a plain object. isPlainObject moves into value.ts so
node.ts can use it without importing resolve-values.ts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: Load wires a resource's slots and resolves values in its props

provision() of a resource now records its slot nodes, input and dependency
edges, in ports and authored edges, and checks its wiring with the rest of
the graph. serviceInputs becomes dependencySlots, over services and
resources. The second pass walks each resource's props; a resource whose
props held a value appears in the graph as a frozen copy that keeps its
prototype and brand.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: a resource descriptor reads its slots through connectionInputs

The inputs half of buildConfig becomes connectionInputs, over a service or
a resource, so a resource descriptor gets the same "did not supply it"
check a service gets. The provisioned-param minting loop accepts a
resource consumer as well as a service.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: tests for a resource with slots wired at construction

resource-slots.test.ts covers the slot node, edges, ports, authored edges,
forwarded module inputs, the construction errors, the cycle check and the
resolved props copy. lowering.test.ts covers a resource descriptor reading
its resolved props and its producer's outputs through connectionInputs.
value.test-d.ts covers the resource factory's types.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 spec records how D2 landed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: provision(resource, { deps }) says a resource is wired at construction

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: module test asserts the new provision(resource, { deps }) text

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* core: a resource from another copy of core with no inputs, deps or props has no slots

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* cli: the environment name reaches Load in the CLI and in the generated stack files

runPipeline takes the environment and passes it to Load; deploy, destroy
and dev pass the one they hold. Both generated stack files render
environment into LowerOptions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* examples/env-param: serve a value computed from the environment's name

The module binds a second input key, environment, to resolve() over
environment.name; the server echoes it and the smoke script asserts it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 spec records how D3 landed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(decisions): align the value amendments with the landed slice

The resolve() amendment uses the glossary's words for a value and its
real value. The public URL amendment says what a resource's deps hold.
The index marks the resolve() and public URL amendments in the house form.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): the glossary and domain docs describe values and Load's second pass

The glossary's Value entry names RealValuesOf. The core model shows
resource() with inputs, deps and props, Load's options and its second
pass over values, and LowerOptions with environment in place of stage.
Module composition adds environment to the module context.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(guides, skill): values that differ per environment

The building guide gains a section on environment.name and resolve():
where a value is accepted, why it is opaque, why provision() inside fn
fails, why fn must be pure, and the errors an author can hit. The
reusable-modules section lists environment among what the builder
receives, and the service input section lists a value as a binding leaf.
The deploying guide's environments section points at it, and the skill
mirrors it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive: record the two debts the values slice leaves

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): keep C1's design reviews apart from its code reviews

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* refactor(core): name the plain-data type PlainData

PlainValue read as a kind of Value, which is the reverse of what it is: the
data a Value may stand for. PlainData matches the error texts and the prose.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* refactor(core): name the slot builder for what it returns

dependencySlots returned graph nodes and edges, not slots, and its error
called a resource a service. It is now slotNodesAndEdges(node, nodeId), and
the error says Node "…" has no inputs map.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* refactor(core): name a props member ResourcePropsMember

ResourceProp is any member at any depth inside props, not a property.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* refactor(core): call a binding site's partition kind, not channel

The guides use "channel" for dependencies versus input. The binding site
splits input, param and props, a different partition.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* refactor(core): derive the environment shapes from EnvironmentFacts

A value origin's fact is now keyof EnvironmentFacts, and one mapped type,
EnvironmentRealValues, gives the real-value shape that LoadOptions and
resolveValues take. A second fact is now one new key.

LoadOptions moves to graph-types.ts and graph.ts re-exports it, so
load-module.ts no longer imports graph.ts, which imports it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* fix(core): a param binding takes a value only for a plain-data param

ParamBindings accepted Value<T> for any param type, so a value standing for
a Date compiled and then always failed at Load. It now accepts a value only
when the param's output type is plain data, as an input binding does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* refactor(core): connectionInputs takes the lowering context

A resource descriptor called connectionInputs with five fields of the
LowerContext it already holds. The exported function now takes the context;
buildConfig keeps its signature and shares a private helper.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* test(core): a provisioned need on a resource slot is minted

The minting loop accepts a resource consumer, but no test reached that
branch. This one wires a resource slot with a provision need and checks the
minted value arrives through connectionInputs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* fix(prisma-cloud): count a resource consumer's provisioned edge

Core mints a value for a resource slot that carries a provision need, but
provisionedEdges skipped every consumer that was not a service, so the
producer would never accept the minted key. The scan now matches core's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* test(core): group the second pass's tests by what they check

The resource-props tests move beside the other second-pass tests, since
props are a binding site, not a slot. The refusals of a value as an id, a
deps entry, a secrets or a params binding run in the first pass, so they get
their own describe block.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* fix(core): seal provision when Load fails before its second pass

If the first pass, the wiring check or the cycle check threw, that Load's
provision stayed open, and a leaked closure could add nodes to a dead Load
without an error. The seal now runs in a finally.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* test(core): pin the param, prop and whole-input binding texts

Only the input-key text was asserted. The param, resource-prop and whole
input binding texts now each have an exact-message case.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* fix(example): the env-param smoke run waits out an older revision

An older revision with the same greeting and no environment field could
still be serving right after a deploy, and the smoke run failed at once.
It now keeps polling on an environment mismatch and prints the FAIL line
only when the deadline passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(core): name every use of deps on a resource's deps field

The comment named only ModuleNode.deps as the clash. module()'s boundary
deps and compute()'s deps declare slots too, while provision()'s and
resource()'s deps bind producers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): a resource class must not use #private fields

The rule lived only in a doc comment on ResourceNodeBase. It is part of the
extension contract, so the core model now states it beside the factories.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(glossary): define a Resource by its lifecycle, and add Driver

The Resource entry led with "a dependency defined by its state", which a
public URL contradicts. It now leads with the lifecycle test its second
paragraph already states, and says "dependency slots" instead of "Inputs".
A short Driver entry defines the role the Resource entry names.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(guides, skill): keep an environment's case until it is destroyed

Destroying an environment runs every resolve() function for it. Removing its
case from a lookup first makes destroy fail at Load and leaves its resources
up, so the guide and the skill say to keep the case until destroy finishes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 spec pins a resource that exposes nothing, and records the fix round

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* feat(core): a resource may expose nothing

resource() and ResourceNodeBase take either provides or type. A resource
built with type alone has no $out port, and provision() returns only its id.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): a resource may expose nothing

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* style(core): format the resource-slots tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* fix(example): the env-param smoke run finds the app by its address

Since every platform name is the node's address, the echo app is named
env-param-example.echo, not echo. The smoke script looked for the old name
and found nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 manual QA record

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* drive(public-urls): C1 QA record notes the teardown

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* fix(core, prisma-cloud): one guard for a resource node with no inputs

A resource node built by another installed copy of core may lack inputs.
Load already treated that as no slots; the minting loop in lowering and
the target's provisioned-edges walk dereferenced inputs unguarded and
would have thrown. slotsOf moves to node.ts and all three use it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* A node depends on an interface or exposes one: retire Input and Output, rename the module body's inputs to deps (#398)

* docs(decisions): a node depends on an interface or exposes one

Input and Output were the words for the two ends of a connection, and
they were ambiguous: the dataflow reading and the ports-and-adapters
reading give the same two ends opposite names. The record settles the
words the authoring surface already uses, deps and expose, with
interface as the noun, and retires the port words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(design): say dependency and exposed interface, never Input or Output

The glossary defines Interface, Dependency and Exposed interface in place
of Input, Output and Port; the domain map, layering map, authoring
surface, example app, principles and domain docs use the same words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* feat(core): a module body receives its dependencies as deps, not inputs

The one user-facing place that called a dependency an input. The shared
modules, the example and the tests follow; the topology's port direction
field says what in and out mean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs(guides, skill): the module builder's deps, and the upgrade note

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* docs, core: the Load errors and the skill say dependency and exposed interface

The error texts still called a declared dependency an input and an exposed
interface an output, and the skill still said ports.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* test(cron): assert the Load error's new wording

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

* test(core): a test title says dependency, not input

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>

---------

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

---------

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants